///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef __BUOLA_CONTAINER_CBUFFER_H__
#define __BUOLA_CONTAINER_CBUFFER_H__

#include <buola/buola.h>

namespace buola {

namespace detail {
    
struct CBufferPrivate : public UShared
{
    CBufferPrivate(std::size_t pSize,std::size_t pElemSize)
        :   mData(alloc_array<uint8_t>(pSize*pElemSize))
        ,   mOwn(true)
        ,   mSize(pSize)
    {}

    CBufferPrivate(uint8_t *pBuffer,std::size_t pSize,bool pOwn)
        :   mData(pBuffer)
        ,   mOwn(pOwn)
        ,   mSize(pSize)
    {}

    ~CBufferPrivate()
    {
        if(mOwn)
            free(mData);
    }

    uint8_t *mData;
    bool mOwn;

    std::size_t mSize; //in elements
    off_t mOffset;
};

/*namespace detail*/ }

DECLARE_NAME(WithPadding);

template<typename tType>
class CBuffer
{
public:
    CBuffer(std::size_t pSize)
        :   mBuffer(new detail::CBufferPrivate(pSize,sizeof(tType)))
    {}

    CBuffer(tType *pData,std::size_t pSize,bool pOwn=false)
        :   mBuffer(new detail::CBufferPrivate((uint8_t*)pData,pSize,pOwn))
    {}

    CBuffer(NWithPadding,std::size_t pSize,std::size_t pPadding)
        :   mBuffer(new detail::CBufferPrivate(pSize+pPadding,sizeof(tType)))
    {
        memset(Data()+pSize,0,pPadding*sizeof(tType));
        mBuffer->mSize=pSize;
    }

    CBuffer(const CBuffer &pBuffer)
        :   mBuffer(pBuffer.mBuffer)
    {}
    
    CBuffer(CBuffer &&pBuffer)
        :   mBuffer(std::move(pBuffer.mBuffer))
    {}
    

    std::size_t Size() const      {   return mBuffer->mSize;      }
    std::size_t ByteSize() const  {   return mBuffer->mSize*sizeof(tType);    }

    void SetSize(std::size_t pSize)
    {
        assert(pSize<=mBuffer->mSize);
        mBuffer->mSize=pSize;
    }

    void SetByteSize(std::size_t pSize)
    {
        SetSize(pSize/sizeof(tType));
    }

    off_t Offset() const   {   return mBuffer->mOffset;    }
    void SetOffset(off_t pOffset)   {   mBuffer->mOffset=pOffset;   }

    tType *Data()    {   return (tType*)mBuffer->mData;  }
    const tType *Data() const    {   return (const tType*)mBuffer->mData;  }

    tType& operator[](size_t i)                 {   return Data()[i];    }
    const tType& operator[](size_t i) const     {   return Data()[i];    }
    
    void CopyDataFrom(const tType *pData,std::size_t pSize)
    {
        memcpy(mBuffer->mData,pData,pSize*sizeof(tType));
    }

    void CopyDataFrom(const tType *pData)
    {
        memcpy(mBuffer->mData,pData,mBuffer->mSize*sizeof(tType));
    }

    void ZeroData()
    {
        memset(mBuffer->mData,0,mBuffer->mSize*sizeof(tType));
    }

private:
    intrusive_ptr<detail::CBufferPrivate> mBuffer;
};

/*namespace buola*/ }

#endif
